package xin.yangshuai.oauth202.controller;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import xin.yangshuai.common01.entity.AuthorizationInfo;
import xin.yangshuai.oauth202.controller.entity.AuthorizeDTO;

import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

/**
 * OAuth2Controller
 *
 * @author shuai
 * @date 2025/2/12
 */
@Controller
@RequestMapping("oauth2")
public class OAuth2Controller {

    private static final Logger logger = LoggerFactory.getLogger(OAuth2Controller.class);

    @Value("${oauth2.authorize-login.username}")
    private String username;

    @Value("${oauth2.authorize-login.password}")
    private String password;

    private Map<String, AuthorizeDTO> authorizeMap = new HashMap<>();
    private Map<String, AuthorizationInfo> accessTokenMap = new HashMap<>();
    private Map<String, AuthorizationInfo> refreshTokenMap = new HashMap<>();

    @GetMapping("authorize")
    public String authorize(AuthorizeDTO authorizeDTO) {

        String clientId = authorizeDTO.getClientId();
        String redirectUrl = authorizeDTO.getRedirectUrl();
        String scope = authorizeDTO.getScope();
        String responseType = authorizeDTO.getResponseType();
        String state = authorizeDTO.getState();

        logger.info("client_id=[{}]", clientId);
        logger.info("redirect_url=[{}]", redirectUrl);
        logger.info("scope=[{}]", scope);
        logger.info("response_type=[{}]", responseType);
        logger.info("state=[{}]", state);

        // 缓存收到的认证信息，重定向到登录页面
        String code = UUID.randomUUID().toString().substring(0, 20);

        authorizeMap.put(code, authorizeDTO);

        return "redirect:/oauth2/authorizeLoginPage?code=" + code;
    }

    @GetMapping("authorizeLoginPage")
    public String authorizeLoginPage(String code, ModelMap modelMap) {

        modelMap.put("code", code);
        return "authorizeLogin";
    }

    @PostMapping("authorizeLogin")
    public String authorizeLogin(String username, String password, String code) throws UnsupportedEncodingException {

        logger.info("username=[{}]", username);
        logger.info("password=[{}]", password);
        logger.info("code=[{}]", code);

        String errorMessage = "";

        if (StringUtils.equals(this.username, username) && StringUtils.equals(this.password, password)) {
            // 登录成功，重定向回第三方应用
            AuthorizeDTO authorizeDTO = authorizeMap.get(code);
            if (authorizeDTO == null) {
                errorMessage = "授权请求已失效";
            } else {
                // 生成access_token
                String accessToken = UUID.randomUUID().toString();
                String refreshToken = UUID.randomUUID().toString();
                String openId = UUID.randomUUID().toString();

                AuthorizationInfo authorizationInfo = new AuthorizationInfo();
                authorizationInfo.setAccessToken(accessToken);
                authorizationInfo.setExpiresIn(1000);
                authorizationInfo.setRefreshToken(refreshToken);
                authorizationInfo.setRefreshExpiresIn(10000);
                authorizationInfo.setOpenId(openId);
                authorizationInfo.setScope("01,02");
                authorizationInfo.setTokenType("bearer");

                String redirectUrl = authorizeDTO.getRedirectUrl();
                String state = authorizeDTO.getState();

                String responseType = authorizeDTO.getResponseType();
                if (StringUtils.equalsIgnoreCase("token", responseType)) {
                    return "redirect:" + redirectUrl + "?accessToken=" + accessToken + "&tokenType=bearer&expiresIn=3600";
                } else if (StringUtils.equalsIgnoreCase("code", responseType)) {
                    // 缓存收到的认证信息，重定向到登录页面
                    String code1 = UUID.randomUUID().toString().substring(0, 20);

                    accessTokenMap.put(code1, authorizationInfo);
                    refreshTokenMap.put(refreshToken, authorizationInfo);

                    return "redirect:" + redirectUrl + "?state=" + state + "&code=" + code1;
                }

            }
        } else {
            errorMessage = "登录认证失败";
        }

        return "redirect:/oauth2/authorizeErrorPage?errorMessage=" + URLEncoder.encode(errorMessage, "utf-8");
    }

    @GetMapping("authorizeErrorPage")
    public String authorizeErrorPage(String errorMessage, ModelMap modelMap) {

        modelMap.put("errorMessage", errorMessage);
        return "authorizeError";
    }

    @ResponseBody
    @GetMapping("accessToken")
    public AuthorizationInfo accessToken(String clientId, String clientSecret, String code, String username, String password, String grantType) {

        logger.info("client_id=[{}]", clientId);
        logger.info("client_secret=[{}]", clientSecret);
        logger.info("code=[{}]", code);
        logger.info("username=[{}]", username);
        logger.info("password=[{}]", password);
        logger.info("grant_type=[{}]", grantType);

        AuthorizationInfo authorizationInfo = null;

        if (StringUtils.equalsIgnoreCase("authorization_code", grantType)) {
            authorizationInfo = accessTokenMap.get(code);
        } else if (StringUtils.equalsIgnoreCase("password", grantType)) {

            // 验证账号密码
            if (StringUtils.equals(this.username, username) && StringUtils.equals(this.password, password)) {
                // 生成access_token
                String accessToken = UUID.randomUUID().toString();
                String refreshToken = UUID.randomUUID().toString();
                String openId = UUID.randomUUID().toString();

                authorizationInfo = new AuthorizationInfo();
                authorizationInfo.setAccessToken(accessToken);
                authorizationInfo.setExpiresIn(1000);
                authorizationInfo.setRefreshToken(refreshToken);
                authorizationInfo.setRefreshExpiresIn(10000);
                authorizationInfo.setOpenId(openId);
                authorizationInfo.setScope("01,02");
                authorizationInfo.setTokenType("bearer");
            }
        } else if (StringUtils.equalsIgnoreCase("client_credentials", grantType)) {

            // 生成access_token
            String accessToken = UUID.randomUUID().toString();
            String refreshToken = UUID.randomUUID().toString();

            authorizationInfo = new AuthorizationInfo();
            authorizationInfo.setAccessToken(accessToken);
            authorizationInfo.setExpiresIn(1000);
            authorizationInfo.setRefreshToken(refreshToken);
            authorizationInfo.setRefreshExpiresIn(10000);
            authorizationInfo.setScope("01,02");
            authorizationInfo.setTokenType("bearer");
        } else if (StringUtils.equalsIgnoreCase("self_credentials", grantType)) {

            // 生成access_token
            String accessToken = UUID.randomUUID().toString();
            String refreshToken = UUID.randomUUID().toString();
            String openId = UUID.randomUUID().toString();

            authorizationInfo = new AuthorizationInfo();
            authorizationInfo.setAccessToken(accessToken);
            authorizationInfo.setExpiresIn(1000);
            authorizationInfo.setRefreshToken(refreshToken);
            authorizationInfo.setRefreshExpiresIn(10000);
            authorizationInfo.setOpenId(openId);
            authorizationInfo.setScope("01,02");
            authorizationInfo.setTokenType("bearer");
        }

        return authorizationInfo;
    }

    @ResponseBody
    @GetMapping("refreshToken")
    public AuthorizationInfo refreshToken(String clientId, String clientSecret, String refreshToken) {

        logger.info("client_id=[{}]", clientId);
        logger.info("client_secret=[{}]", clientSecret);
        logger.info("refresh_token=[{}]", refreshToken);

        AuthorizationInfo authorizationInfo = refreshTokenMap.get(refreshToken);

        return authorizationInfo;
    }
}
